//
//  convert.h
//  utilities
//
//  Created by Sam Jaffe on 11/12/12.
//  Copyright (c) 2012 Sam Jaffe. All rights reserved.
//

#pragma once

#include <vector>
#include <list>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <deque>
#include <forward_list>

namespace detail {
    template <typename Key, typename... Args>
    struct chain;
    
    template <typename Value>
    struct chain_impl<Value> {
        using asmap = Value;
        using asunordered_map = Value;
    };
    
    template <typename Key, typename... Args>
    struct chain_impl {
#ifdef _STL_MAP_H
        using asmap = std::map<Key, typename chain_impl<Args...>::asmap >;
#endif
#ifdef _UNORDERED_MAP_H
        using asunordered_map = std::unordered_map<Key, typename chain_impl<Args...>::asmap >;
#endif
    };
}

#ifdef _STL_MAP_H
template <typename Key, typename... Args>
using chainmap = detail::chain_impl<Key, Args...>::asmap;
#endif
#ifdef _UNORDERED_MAP_H
template <typename Key, typename... Args>
using unordered_chainmap = detail::chain_impl<Key, Args...>::asunordered_map;
#endif

namespace std {
#ifdef _STL_VECTOR_H
    template <class Entry, class Container>
    vector<Entry> tovector(const Container& l) {
        vector<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.push_back(t);});
        return v;
    }
#endif
#ifdef _STL_LIST_H
    template <class Entry, class Container>
    list<Entry> tolist(const Container& l) {
        list<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.push_back(t);});
        return v;
    }
#endif
#ifdef _FORWARD_LIST_H
    template <class Entry, class Container>
    forward_list<Entry> toforward_list(const Container& l) {
        forward_list<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.push_front(t);});
        return v;
    }
#endif
#ifdef _STL_DEQUE_H
    template <class Entry, class Container>
    deque<Entry> todeque(const Container& l) {
        deque<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.push_back(t);});
        return v;
    }
#endif
#ifdef _STL_SET_H
    template <class Entry, class Container>
    set<Entry> toset(const Container& l) {
        set<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.insert(t);});
        return v;
    }

    template <class Entry, class Container>
    multiset<Entry> tomultiset(const Container& l) {
        multiset<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.insert(t);});
        return v;
    }
#endif
#ifdef _STL_MAP_H
    template <class Key, class Value, class Container>
    map<Key, Value> tomap(const Container& l) {
        map<Key, Value> m;
        for_each(l.begin(), l.end(), [&m](const pair<Key, Value>& p) {m.insert(p);});
        return m;
    }
    
    template <class Key, class Value, class Container>
    multimap<Key, Value> tomultimap(const Container& l) {
        multimap<Key, Value> m;
        for_each(l.begin(), l.end(), [&m](const pair<Key, Value>& p) {m.insert(p);});
        return m;
    }
#endif
#ifdef _UNORDERED_SET_H
    template <class Entry, class Container>
    unordered_set<Entry> tounordered_set(const Container& l) {
        unordered_set<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.insert(t);});
        return v;
    }

    template <class Entry, class Container>
    unordered_multiset<Entry> tounordered_multiset(const Container& l) {
        unordered_multiset<Entry> v;
        for_each(l.begin(), l.end(), [&v](const Entry& t) {v.insert(t);});
        return v;
    }
#endif
#ifdef _UNORDERED_MAP_H
    template <class Key, class Value, class Container>
    unordered_map<Key, Value> tounordered_map(const Container& l) {
        unordered_map<Key, Value> m;
        for_each(l.begin(), l.end(), [&m](const pair<Key, Value>& p) {m.insert(p);});
        return m;
    }
    
    template <class Key, class Value, class Container>
    unordered_multimap<Key, Value> tounordered_multimap(const Container& l) {
        unordered_multimap<Key, Value> m;
        for_each(l.begin(), l.end(), [&m](const pair<Key, Value>& p) {m.insert(p);});
        return m;
    }
#endif
}
